﻿using FundHelper.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Runtime.Intrinsics.X86;
using System.Security.Cryptography;
using System.Text;

namespace FundHelper.Model
{
    public static partial class Indicator
    {

        //RSV:=(CLOSE-LLV(LOW, N))/(HHV(HIGH, N)-LLV(LOW, N))*100;
        //K:SMA(RSV, M1,1);
        //D:SMA(K, M2,1);
        //J:3*K-2*D;
        /// <summary>
        /// 计算KDJ
        /// </summary>
        /// <param name="list"></param>
        /// <param name="N">周期，默认9</param>
        /// <param name="M1">K周期，默认3</param>
        /// <param name="M2">D周期，默认3</param>
        /// <returns>返回KDJ，按照时间递增，现在处于最后面</returns>
        /// <exception cref="ArgumentException"></exception>
        public static IEnumerable<KDJResult> GetKDJResult(this IEnumerable<Quote> list, int N = 9,
                int M1 = 3,
                int M2 = 3)
        {
            var tuples = list.ToList();
            if (tuples.Count < N * 3)
                throw new ArgumentException("无足够的数据计算KDJ,>tuples.count>3*N>N>M1=>M2");
            var KDJList = new List<KDJResult>();
            //EMa RSVs会把现在放在最后面
            var RSVs = CalRsv(tuples, N);
            var Ks = RSVs.Select(a => (a.Date, a.Value)).GetSMAResult(M1);
            var Ds = Ks.Select(a => (a.dateTime, a.Value)).GetSMAResult(M2);
            var RsvArr = RSVs.Reverse().ToArray();
            var KArr = Ks.Reverse().ToArray();
            var DArr = Ds.Reverse().ToArray();

            for (int i = 0; i < RsvArr.Count() && i < KArr.Count() && i < DArr.Count(); i++)
            {
                var date = RsvArr[i].Date;
                var Rsv = RsvArr[i].Value;
                var K = KArr[i].Value;
                var D = DArr[i].Value;
                var J = 3 * K - 2 * D;
                KDJList.Add(new KDJResult(date, Rsv, K, D, J));
            }
            return KDJList.OrderBy(a => a.DateTime);
        }

        record RsvResult(DateTime Date, double Value);
        record KResult(DateTime Date, double Value);
        record DResult(DateTime Date, double Value);

        private static IEnumerable<RsvResult> CalRsv(this IEnumerable<Quote> list, int period)
        {
            var tuples = list.ToList();
            if (tuples.Count < period)
                throw new ArgumentException("Not enough data points for the specified RSV period.");
            var RsvArry = new RsvResult[tuples.Count - period];
            var temp = tuples.OrderBy(a => a.Date).ToList();
            var range = new Range();
            for (int i = period; i < temp.Count; i++)
            {
                var date = temp[i].Date;
                range = new Range(i - period + 1, i + 1);
                var dates = temp.Take(range).Select(a => a.Date);
                var prices = temp.Take(range).Select(a => a.Price);
                var lows = temp.Take(range).Select(a => a.Low);
                var highs = temp.Take(range).Select(a => a.High);

                var lowestLow = lows.Min();
                var highestHigh = highs.Max();
                var price = prices.Last();
                double rsv = 0;
                if (lowestLow != highestHigh)
                    rsv = (price - lowestLow) / (highestHigh - lowestLow) * 100f;
                RsvArry[i - period] = new RsvResult(temp[i].Date, rsv);
            }
            return RsvArry.AsEnumerable();
        }
        private static IEnumerable<KResult> CalK(this IEnumerable<(DateTime date, double price)> list, int period)
        {
            return list.GetEmaResult(3).Select(a => new KResult(a.dateTime, a.Value));
        }
        private static IEnumerable<DResult> CalD(this IEnumerable<(DateTime date, double price)> list, int period)
        {
            return list.GetEmaResult(3).Select(a => new DResult(a.dateTime, a.Value));
        }
    }


    public class KDJResult(DateTime dateTime, double rsv, double k, double d, double j) : IndicatorResult(dateTime, j)
    {

        [IndicatorName("RSV")]
        public double RSV => rsv;
        [IndicatorName("K")]
        public double K => k;
        [IndicatorName("D")]
        public double D => d;
        [IndicatorName("J")]
        public double J => base.Value;
        public void Deconstruct(out double K, out double D, out double J)
        {
            K = this.K; D = this.D; J = this.J;
        }

    }
    internal class KDJResultCollection : LagCollection<KDJResult>
    {
        public KDJResultCollection(IEnumerable<KDJResult> collection) : base(collection)
        {
        }
        internal static KDJResultCollection Create(IEnumerable<ICanToTuple> his)
        {
            var tupleList = his.ToQuote();
            var kdjResultArry = tupleList.GetKDJResult(9, 3, 3).Reverse().ToArray();
            return new KDJResultCollection(kdjResultArry);
        }
    }

}
